﻿using Basler.Pylon;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;

namespace Camera
{
    public class CameraBasler : CameraBase
    {
        private Basler.Pylon.Camera camera;
        private PixelDataConverter converter;
        private Stopwatch stopWatch;
        private string serialNumber;
        private bool m_bDisConnect;

        public override event ReadImageDelegate ReadImageEvent;
        public override event ConnectLossDelegate ConnectLossEvent;

        public CameraBasler()
        {
            converter = new PixelDataConverter();
            stopWatch = new Stopwatch();
        }

        public static string[] DeviceListAcq()
        {
            // Ask the camera finder for a list of camera devices.
            List<ICameraInfo> allCameras = CameraFinder.Enumerate();
            // Ask the camera finder for a list of all GigE camera devices. 
            // Note that this sample only works with GigE camera devices. 
            string[] deviceList = new string[allCameras.Count];
            // Loop over all cameras found.
            for (int i = 0; i < allCameras.Count; ++i)
            {
                deviceList[i] = allCameras[i][CameraInfoKey.SerialNumber];
            }
            return deviceList;
        }

        // Closes the camera object and handles exceptions.
        private void DestroyCamera()
        {
            // Destroy the camera object.
            if (camera != null)
            {
                try
                {
                    camera.Close();
                    camera.Dispose();
                }
                catch (Exception)
                {

                }
                camera = null;
            }
            // Destroy the converter object.
            if (converter != null)
            {
                converter.Dispose();
                converter = null;
            }
        }

        // Occurs when a device with an opened connection is removed.
        private void OnConnectionLost(object sender, EventArgs e)
        {
            ConnectLossEvent("相机 " + serialNumber + " 异常掉线，重新连接中。。。");
            m_bDisConnect = false;
            // Close the camera object.
            DestroyCamera();
            while (!m_bDisConnect)
            {
                // Open the connection to the selected camera device.
                try
                {
                    // Create a new camera object.
                    camera = new Basler.Pylon.Camera(serialNumber);
                    // Register the standard configuration event handler for setting up the camera for continuous acquisition.
                    camera.CameraOpened += Configuration.AcquireContinuous;
                    // Register for the events of the image provider needed for proper operation.
                    camera.ConnectionLost += OnConnectionLost;
                    camera.CameraOpened += OnCameraOpened;
                    camera.CameraClosed += OnCameraClosed;
                    camera.StreamGrabber.GrabStarted += OnGrabStarted;
                    camera.StreamGrabber.ImageGrabbed += OnImageGrabbed;
                    camera.StreamGrabber.GrabStopped += OnGrabStopped;
                    // Open the connection to the camera device.
                    camera.Open(1000, TimeoutHandling.Return);
                    camera.StreamGrabber.Start(GrabStrategy.LatestImages, GrabLoop.ProvidedByStreamGrabber);
                }
                catch (Exception)
                {
                    continue;
                }
                m_bDisConnect = true;
            }
        }

        // Occurs when the connection to a camera device is opened.
        private void OnCameraOpened(object sender, EventArgs e)
        {

        }

        // Occurs when the connection to a camera device is closed.
        private void OnCameraClosed(object sender, EventArgs e)
        {

        }

        // Occurs when a camera starts grabbing.
        private void OnGrabStarted(object sender, EventArgs e)
        {
            // Reset the stopwatch used to reduce the amount of displayed images. The camera may acquire images faster than the images can be displayed.
            stopWatch.Reset();
        }

        // Occurs when an image has been acquired and is ready to be processed.
        private void OnImageGrabbed(object sender, ImageGrabbedEventArgs e)
        {
            try
            {
                // Acquire the image from the camera. Only show the latest image. The camera may acquire images faster than the images can be displayed.
                // Get the grab result.
                IGrabResult grabResult = e.GrabResult;
                // Check if the image can be displayed.
                // Reduce the number of displayed images to a reasonable amount if the camera is acquiring images very fast.
                if (grabResult.IsValid && (!stopWatch.IsRunning || stopWatch.ElapsedMilliseconds > 33))
                {
                    stopWatch.Restart();
                    //相机像素数据
                    byte[] buffer = grabResult.PixelData as byte[];
                    //锁定像素数据
                    GCHandle hand = GCHandle.Alloc(buffer, GCHandleType.Pinned);
                    //获取像素数据的指针
                    IntPtr pr = hand.AddrOfPinnedObject();
                    //转成灰度图HObject
                    //HOperatorSet.GenImage1(out HObject image, new HTuple("byte"), grabResult.Width, grabResult.Height, pr);
                    //释放内存
                    if (hand.IsAllocated)
                    {
                        hand.Free();
                    }
                    Imgdata imgdata = new Imgdata
                    {
                        data = pr,
                        width = grabResult.Width,
                        height = grabResult.Height
                    };
                    ReadImageEvent(imgdata);
                }
            }
            catch (Exception)
            {

            }
            finally
            {
                // Dispose the grab result if needed for returning it to the grab loop.
                e.DisposeGrabResultIfClone();
            }
        }

        // Occurs when a camera has stopped grabbing.
        private void OnGrabStopped(object sender, GrabStopEventArgs e)
        {
            // Reset the stopwatch.
            stopWatch.Reset();
            // If the grabbed stop due to an error, display the error message.
        }

        public override bool Open(string serialNumber)
        {
            this.serialNumber = serialNumber;
            // Ask the camera finder for a list of camera devices.
            CameraFinder.Enumerate();
            // Destroy the old camera object.
            if (camera != null)
            {
                DestroyCamera();
            }
            // Open the connection to the selected camera device.
            try
            {
                // Create a new camera object.
                camera = new Basler.Pylon.Camera(serialNumber);
                // Register the standard configuration event handler for setting up the camera for continuous acquisition.
                camera.CameraOpened += Configuration.AcquireContinuous;
                // Register for the events of the image provider needed for proper operation.
                camera.ConnectionLost += OnConnectionLost;
                camera.CameraOpened += OnCameraOpened;
                camera.CameraClosed += OnCameraClosed;
                camera.StreamGrabber.GrabStarted += OnGrabStarted;
                camera.StreamGrabber.ImageGrabbed += OnImageGrabbed;
                camera.StreamGrabber.GrabStopped += OnGrabStopped;
                // Open the connection to the camera device.
                camera.Open(1000, TimeoutHandling.Return);
            }
            catch (Exception)
            {
                return false;
            }
            return true;
        }

        public override bool Close()
        {
            DestroyCamera();
            return true;
        }

        public override void SetTriggerMode(TriggerMode triggerMode)
        {
            if (camera != null && camera.Parameters[PLCamera.TriggerMode].IsWritable)
            {
                camera.Parameters[PLCamera.TriggerMode].SetValue(triggerMode.ToString());
            }
        }

        public override void SetTriggerSource(TriggerSource triggerSource)
        {
            if (camera == null && camera.Parameters[PLCamera.TriggerSource].IsWritable)
            {
                return;
            }
            string value;
            switch (triggerSource)
            {
                case TriggerSource.Line1:
                    value = PLCamera.TriggerSource.Line1;
                    break;
                case TriggerSource.Software:
                    value = PLCamera.TriggerSource.Software;
                    break;
                default:
                    return;
            }
            camera.Parameters[PLCamera.TriggerSource].SetValue(value);
        }

        public override bool TriggerSoftware()
        {
            try
            {
                if (camera.WaitForFrameTriggerReady(1000, TimeoutHandling.ThrowException))
                {
                    camera.ExecuteSoftwareTrigger();
                }
            }
            catch (Exception)
            {
                return false;
            }
            return true;
        }

        public override string GetSerialNumber()
        {
            return serialNumber;
        }

        public override bool StartGrab()
        {
            try
            {
                camera.StreamGrabber.Start(GrabStrategy.LatestImages, GrabLoop.ProvidedByStreamGrabber);
            }
            catch (Exception)
            {
                return false;
            }
            return true;
        }

        public override void StopGrab()
        {
            // Stop the grabbing.
            try
            {
                camera.StreamGrabber.Stop();
            }
            catch (Exception)
            {

            }
        }

        public override double GetParameter(string key)
        {
            if (!camera.Parameters.Contains(new FloatName(key)))
            {
                key += "Abs";
            }
            return camera.Parameters[new FloatName(key)].GetValue();
        }

        public override void SetParameter(string key, double fValue)
        {
            if (!camera.Parameters.Contains(new FloatName(key)))
            {
                key += "Abs";
            }
            try
            {
                camera.Parameters[new FloatName(key)].SetValue(fValue);
            }
            catch (Exception)
            {

            }
        }

        public override void UserSave()
        {
            camera.Parameters[PLCamera.UserSetSelector].SetValue(PLCamera.UserSetSelector.UserSet1);
            camera.Parameters[PLCamera.UserSetSave].Execute();
        }

        public override void LoadConfigurationFromFile(string fileName)
        {

        }
    }
}
